package com.mrtaohuan.servlet.filter;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import lombok.extern.java.Log;

@Log
public class GzipFilter implements Filter {

	@Override
	public void destroy() {

	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		final HttpServletRequest request = (HttpServletRequest) req;

		final String acceptEncoding = request.getHeader("Accept-Encoding");
		boolean supportGZIP = false;

		if (null != acceptEncoding && 0 <= acceptEncoding.indexOf("gzip")) {
			supportGZIP = true;
		}

		if (!supportGZIP) {
			log.info("Gzip NOT be supported");
			chain.doFilter(request, resp);

			return;
		}

		final HttpServletResponse response = (HttpServletResponse) resp;

		response.addHeader("Content-Encoding", "gzip");
		response.addHeader("Vary", "Accept-Encoding");
		chain.doFilter(request, new GZIPServletResponseWrapper(response));

	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
	}

	private class GZIPServletResponseWrapper extends HttpServletResponseWrapper {

		/**
		 * GZIP output stream.
		 */
		private GZIPOutputStream gzipStream;

		/**
		 * Servlet output stream.
		 */
		private ServletOutputStream servletOutputStream;

		/**
		 * Print writer.
		 */
		private PrintWriter printWriter;

		/**
		 * Constructs an {@link GZIPServletResponseWrapper} object with the
		 * specified http servlet response.
		 * 
		 * @param httpServletResponse
		 *            the specified http servlet response
		 * @throws IOException
		 *             io exception
		 */
		GZIPServletResponseWrapper(final HttpServletResponse httpServletResponse)
				throws IOException {
			super(httpServletResponse);
		}

		@Override
		public ServletOutputStream getOutputStream() throws IOException {
			if (null == servletOutputStream) {
				servletOutputStream = createOutputStream();
			}

			return servletOutputStream;
		}

		@Override
		public PrintWriter getWriter() throws IOException {
			if (null == printWriter) {
				printWriter = new PrintWriter(new OutputStreamWriter(
						getOutputStream(), getCharacterEncoding()));
			}

			return printWriter;
		}

		/**
		 * Creates output stream with GZIP delegation.
		 * 
		 * @return servlet output stream
		 * @throws IOException
		 *             io exception
		 */
		private ServletOutputStream createOutputStream() throws IOException {
			final ServletResponse servletResponse = this.getResponse();

			gzipStream = new GZIPOutputStream(servletResponse.getOutputStream());

			return new ServletOutputStream() {

				@Override
				public void write(final int b) throws IOException {
					gzipStream.write(b);
				}

				@Override
				public void flush() throws IOException {
					gzipStream.flush();
				}

				@Override
				public void close() throws IOException {
					gzipStream.close();
				}

				/*
				 * These two are not absolutely needed. They are here simply
				 * because they were overriden by GZIPOutputStream.
				 */
				@Override
				public void write(final byte[] b) throws IOException {
					gzipStream.write(b);
				}

				@Override
				public void write(final byte[] b, final int off, final int len)
						throws IOException {
					gzipStream.write(b, off, len);
				}
			};
		}
	}

}
